package com.wang.jmonkey.interceptor;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.wang.jmonkey.common.result.HttpResult;
import com.wang.jmonkey.common.utils.UserUtil;
import com.wang.jmonkey.interceptor.handler.result.ExceptionResultHandlerManager;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @Description Controller拦截器
 * @Author HeJiawang
 * @Date 2021/5/20 13:08
 */
@Slf4j
public class ControllerInterceptor implements MethodInterceptor {

    private static final String SUBMIT_KEY = "submit";
    private static final String SUBMIT_ERROR_MSG = "请勿重复提交";

    private GenericApplicationContext context;
    private RedisTemplate<String, Object> redis;

    public ControllerInterceptor(GenericApplicationContext context, RedisTemplate<String, Object> redis) {
        this.context = context;
        this.redis = redis;
    }

    /**
     * <li>1、校验重复提交</li>
     * @param invocation invocation
     * @return result
     */
    @Override
    public Object invoke(MethodInvocation invocation) {
        String submitKey = StrUtil.EMPTY;

        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (ObjectUtil.isNotEmpty(attributes)) {
                HttpServletRequest request = attributes.getRequest();

                String requestURI = request.getRequestURI(), username = UserUtil.Info.getUsername(request);
                if (StrUtil.isNotEmpty(username) && ! request.getMethod().equalsIgnoreCase(RequestMethod.GET.name())) {
                    submitKey = String.format( "%s:%s_%s", SUBMIT_KEY, username, requestURI);

                    if (redis.hasKey(submitKey)) {
                        return new HttpResult<>().error(SUBMIT_ERROR_MSG);
                    } else {
                        String redisKey = submitKey;
                        redis.execute((RedisCallback<Long>) connection -> {
                            connection.set(redisKey.getBytes(), SUBMIT_KEY.getBytes());
                            connection.expire(redisKey.getBytes(), 60);
                            connection.close();

                            return null;
                        });
                    }
                }
            }

            return invocation.proceed();
        } catch (Throwable e) {
            log.error(this.getClass().getName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName(), e);

            ExceptionResultHandlerManager manager = this.context.getBean(ExceptionResultHandlerManager.class);
            return manager.result(invocation.getMethod().getReturnType(), e.getMessage(), manager);
        } finally {
            redis.delete(submitKey);
        }
    }
}
